//
//  AppDelegate.m
//  FMDB的使用
//
//  Created by caominglei on 2018/2/4.
//  Copyright © 2018年 caominglei. All rights reserved.
//

#import "AppDelegate.h"
#import <FMDatabase.h>
#import <FMDatabaseQueue.h>
typedef NS_ENUM(NSInteger, DBVersion) {
    DBVersionV1,
    DBVersionV2,    //历史版本
    DBVersionV3,    //当前版本
};

static NSString *const DBVersionNum = @"DBVersionNum";
static NSString *const dbPath = @"/tmp/tmp.db"; //数据库地址
static NSString *const createTable = @"create table if not exists t1("
"id integer PRIMARY KEY AUTOINCREMENT NOT NULL,"
"name char(50)," "sex char(4),"
"recordDate TIMESTAMP default (datetime('now', 'localtime')))";

/**
 首先定义了一个枚举，标识着当前一共有多少数据库版本变更。（客户端数据库结构更改不会太频繁，如果更改太快，可能意味着初期表设计不合理），此次模拟共三个版本的数据库。
 采用FMDatabaseQueue 进行数据库操作的管理
 */
@interface AppDelegate ()
{
    FMDatabaseQueue *_queue;
}
@end

@implementation AppDelegate

- (instancetype)init{
    if (self = [super init]) {
        _queue = [FMDatabaseQueue databaseQueueWithPath:dbPath];
    }
    return self;
}

/* *
 需要初始化表结构时，调用此方法
 在这里判断DBVersionNum系统之前是否存储过，
 没有存储说明是第一次安装，则进行首次创建表处理。
 有说明之前数据库存在，进行数据库表结构更改。如果是v1版本的数据库 先从v1升级到v2，在从v2升级到v3，以此类推。
 把 DBVersionNum的值写为V3版本 方便下次对比。
 */
- (void)newDBVersionInit
{
    if (![[NSUserDefaults standardUserDefaults] objectForKey:DBVersionNum]) { //系统之前没有数据库 新建立表。
        [self createTables];
        
    }else{
        DBVersion ver = [[[NSUserDefaults standardUserDefaults] objectForKey:DBVersionNum] integerValue];
        switch (ver) {
            case DBVersionV1:{
                [self v1ToV2];
                
            }
            case DBVersionV2:{
                [self v2Tov3];
                
            }
            case DBVersionV3:{
                
            }
                break;
            default:
                break;
                
        }
        
    }
    
}

/* * 创建新表 */
- (void)createTables{
    [_queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
        @try {
            [db executeUpdate:createTable];
            
        } @catch (NSException *exception) {
            *rollback = YES;
            
        }
        
    }];
    [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithInteger:DBVersionV3] forKey:DBVersionNum];
    NSLog(@"NSUserDefaults standardUserDefaults:%@",[[NSUserDefaults standardUserDefaults] objectForKey:DBVersionNum]);
    
}

/* * 版本1 向 版本2 数据迁移 */
- (void)v1ToV2{
    [_queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
        @try { //将原始表名T1 修改为 tempT1
            NSString *renameString = @"alter table t1 rename to tempT1";
            [db executeUpdate:renameString]; //创建新表T1（V2版本的新表创建）
            [db executeUpdate:createTable]; //迁移数据
            NSString *toString = @"insert into t1(name,sex) select name,sex from tempT1";
            [db executeUpdate:toString]; //删除tempT1临时表
            NSString *dropTableStr1 = @"drop table tempT1";
            [db executeUpdate:dropTableStr1];
            
        } @catch (NSException *exception) {
            *rollback = YES;
            
        }
        
    }] ;
    [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithInteger:DBVersionV2] forKey:DBVersionNum];
    
}

- (void)v2Tov3{
    [_queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
        //和 v1ToV2 流程一样
        NSString *renameString = @"alter table t1 rename to tempT1";
        [db executeUpdate:renameString]; //创建新表T1（V2版本的新表创建）
        [db executeUpdate:createTable]; //迁移数据
        NSString *toString = @"insert into t1(name,sex) select name,sex from tempT1";
        [db executeUpdate:toString]; //删除tempT1临时表
        NSString *dropTableStr1 = @"drop table tempT1";
        [db executeUpdate:dropTableStr1];
    }] ;
    [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithInteger:DBVersionV3] forKey:DBVersionNum];
    
}

/**
 上文提到的数据迁移流程就是如此
 */


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    [self newDBVersionInit];
    return YES;
}


- (void)applicationWillResignActive:(UIApplication *)application {
    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
    // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}


- (void)applicationDidEnterBackground:(UIApplication *)application {
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}


- (void)applicationWillEnterForeground:(UIApplication *)application {
    // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}


- (void)applicationDidBecomeActive:(UIApplication *)application {
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}


- (void)applicationWillTerminate:(UIApplication *)application {
    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
    // Saves changes in the application's managed object context before the application terminates.
    [self saveContext];
}


#pragma mark - Core Data stack

@synthesize persistentContainer = _persistentContainer;

- (NSPersistentContainer *)persistentContainer {
    // The persistent container for the application. This implementation creates and returns a container, having loaded the store for the application to it.
    @synchronized (self) {
        if (_persistentContainer == nil) {
            _persistentContainer = [[NSPersistentContainer alloc] initWithName:@"FMDB___"];
            [_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {
                if (error != nil) {
                    // Replace this implementation with code to handle the error appropriately.
                    // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
                    
                    /*
                     Typical reasons for an error here include:
                     * The parent directory does not exist, cannot be created, or disallows writing.
                     * The persistent store is not accessible, due to permissions or data protection when the device is locked.
                     * The device is out of space.
                     * The store could not be migrated to the current model version.
                     Check the error message to determine what the actual problem was.
                    */
                    NSLog(@"Unresolved error %@, %@", error, error.userInfo);
                    abort();
                }
            }];
        }
    }
    
    return _persistentContainer;
}

#pragma mark - Core Data Saving support

- (void)saveContext {
    NSManagedObjectContext *context = self.persistentContainer.viewContext;
    NSError *error = nil;
    if ([context hasChanges] && ![context save:&error]) {
        // Replace this implementation with code to handle the error appropriately.
        // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
        NSLog(@"Unresolved error %@, %@", error, error.userInfo);
        abort();
    }
}

@end
